package com.zh.esapi.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.zh.esapi.entity.EsNews;
import com.zh.esapi.service.ElasticsearchService;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedAvg;
import org.elasticsearch.search.aggregations.metrics.ParsedSum;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author ZH
 */
@Service
public class ElasticsearchServiceImpl implements ElasticsearchService {

    private static final String indexName = "weichai_dev";

    private static final Float minScore = -1f;

    @Autowired
    RestHighLevelClient restHighLevelClient;

    @Override
    public List<Map<String, Object>> searchSort(String title, String summary) throws IOException {
        SearchRequest searchRequest = new SearchRequest(indexName);
        //构建搜索
        MatchQueryBuilder matchQueryBuilder1 = QueryBuilders.matchQuery("title", title);
        MatchQueryBuilder matchQueryBuilder2 = QueryBuilders.matchQuery("summary", summary);
        BoolQueryBuilder must = QueryBuilders.boolQuery().must(matchQueryBuilder1).should(matchQueryBuilder2);
        // 排序条件
        FieldSortBuilder timeSortBuilder = SortBuilders.fieldSort("sourceTime").order(SortOrder.DESC);
        // 默认score是倒序
        ScoreSortBuilder scoreSortBuilder = new ScoreSortBuilder();
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .query(must)
//                .query(matchQuery)
                .timeout(TimeValue.timeValueSeconds(10))
                .sort(timeSortBuilder) //先根据时间排序
                .sort(scoreSortBuilder) //再根据得分排序
                .size(10);
        //执行搜索
        searchRequest.source(searchSourceBuilder);
        SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = search.getHits();
        List<Map<String ,Object>> list = new ArrayList<>();
        for (SearchHit hit : hits) {
            if (hit.getScore() >= minScore){
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                sourceAsMap.put("source",hit.getScore());
                list.add(sourceAsMap);
            }
        }
        System.out.println("搜索出了"+hits.getHits().length+"条");
        return list;
    }

    @Override
    public List<Map<String, Object>> search(String title,String summary) throws IOException {
        SearchRequest searchRequest = new SearchRequest(indexName);
        //构建搜索
        MatchQueryBuilder matchQueryBuilder1 = QueryBuilders.matchQuery("title", title);
        MatchQueryBuilder matchQueryBuilder2 = QueryBuilders.matchQuery("summary", summary);
        BoolQueryBuilder must = QueryBuilders.boolQuery().must(matchQueryBuilder1).should(matchQueryBuilder2);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .query(must)
//                .query(matchQuery)
                .timeout(TimeValue.timeValueSeconds(10))
                .size(10);
        //执行搜索
        searchRequest.source(searchSourceBuilder);
        SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = search.getHits();
        List<Map<String ,Object>> list = new ArrayList<>();
        for (SearchHit hit : hits) {
            if (hit.getScore() >= minScore){
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                sourceAsMap.put("source",hit.getScore());
                list.add(sourceAsMap);
            }
        }
        System.out.println("搜索出了"+hits.getHits().length+"条");
        return list;
    }

    @Override
    public String save(EsNews esNews) throws IOException {
        IndexRequest request = new IndexRequest(indexName);
        request.id(esNews.getId());
        request.source(JSONObject.toJSONString(esNews),XContentType.JSON);
        IndexResponse index = restHighLevelClient.index(request, RequestOptions.DEFAULT);
        return index.getId();
    }

    @Override
    public String deleted(String id) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest(indexName,id);
        DeleteResponse delete = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        return delete.status().toString();
    }

    @Override
    public String update(EsNews esNews) throws IOException {
        UpdateRequest request = new UpdateRequest(indexName, esNews.getId());
        request.doc(JSONObject.toJSONString(esNews),XContentType.JSON);
        UpdateResponse update = restHighLevelClient.update(request, RequestOptions.DEFAULT);
        return update.status().toString();
    }

    @Override
    public EsNews getById(String id) throws IOException {
        GetRequest getRequest = new GetRequest(indexName, id);
        GetResponse response = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        EsNews esNews = JSONObject.parseObject(response.getSourceAsString(), EsNews.class);
        return esNews;
    }

    @Override
    public List<EsNews> getByIds(List<String> idList) throws Exception {
        SearchRequest searchRequest = new SearchRequest(indexName);
        TermsQueryBuilder query = QueryBuilders.termsQuery("_id", idList);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .query(query)
                .timeout(TimeValue.timeValueSeconds(10))
                .size(10000);
        //执行搜索
        searchRequest.source(searchSourceBuilder);
        SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = search.getHits();
        List<EsNews> resList = new ArrayList<>();
        for (SearchHit hit : hits) {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            EsNews esNews = new EsNews();
            //BeanUtils.populate(esNews,sourceAsMap);
            resList.add(esNews);
        }
        return resList;
    }


    @Override
    public List<Map<String, Object>> searchHighLight(String title,String summary) throws IOException {
        SearchRequest request = new SearchRequest(indexName);
        MatchQueryBuilder matchQueryBuilder1 = QueryBuilders.matchQuery("title", title);
        MatchQueryBuilder matchQueryBuilder2 = QueryBuilders.matchQuery("summary", summary);
        BoolQueryBuilder must = QueryBuilders.boolQuery().must(matchQueryBuilder1).should(matchQueryBuilder2);
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title").preTags("<span style='color:red'>").postTags("</span>");
        highlightBuilder.field("summary").preTags("<span style='color:red'>").postTags("</span>");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .query(must)
//                .query(matchQuery)
                .timeout(TimeValue.timeValueSeconds(10))
                .highlighter(highlightBuilder)
                .size(10);
        request.source(searchSourceBuilder);
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

        //分析结果
        List<Map<String,Object>> list = new ArrayList<>();
        for (SearchHit hit : response.getHits()) {
            // 解析高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField restitle = highlightFields.get("title");
            HighlightField resSummary = highlightFields.get("summary");
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            if (restitle != null){
                // 将原来的字段 换位新的高亮字段
                Text[] fragments = restitle.getFragments();
                String newTitle = "";
                for (Text fragment : fragments) {
                    newTitle+=fragment;
                }
                sourceAsMap.put("title",newTitle); // 替换原先的字段
            }
            if (resSummary != null){
                // 将原来的字段 换位新的高亮字段
                Text[] fragments = resSummary.getFragments();
                String newContentText = "";
                for (Text fragment : fragments) {
                    newContentText+=fragment;
                }
                sourceAsMap.put("summary",newContentText); // 替换原先的字段
            }
            list.add(sourceAsMap);
        }
        System.out.println("搜索出了"+list.size());
        return list;
    }

    @Override
    public List<EsNews> searchAll() throws Exception {
        SearchRequest request = new SearchRequest(indexName);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .timeout(TimeValue.timeValueSeconds(10))
                .sort("fetchTime", SortOrder.DESC)
                .size(3000);
        request.source(searchSourceBuilder);
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        List<EsNews> resList = new ArrayList<>();
        for (SearchHit hit : hits) {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            EsNews esNews = new EsNews();
            //BeanUtils.populate(esNews,sourceAsMap);
            resList.add(esNews);
        }
        return resList;
    }

    @Override
    public boolean updateBatchById(List<EsNews> list) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("10s");
        for (EsNews esNews : list) {
            UpdateRequest request = new UpdateRequest(indexName, esNews.getId());
            request.doc(JSONObject.toJSONString(esNews),XContentType.JSON);
            bulkRequest.add(request);
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        return !bulk.isFragment();
    }

    @Override
    public Map<String,Object> aggsByField(String field) throws IOException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        TermsAggregationBuilder aggregation = AggregationBuilders.terms(field).field(field)
                .subAggregation(AggregationBuilders.sum("sum_id").field("id")).  //求和
                subAggregation(AggregationBuilders.avg("avg_id").field("id")); //求平均值
        sourceBuilder.aggregation(aggregation);
        sourceBuilder.size(0);
        SearchRequest searchRequest = new SearchRequest().indices(indexName).source(sourceBuilder);
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        Aggregations aggregations = response.getAggregations();
        ParsedStringTerms parsedStringTerms = aggregations.get(field);
        Map<String,Object> map = new HashMap<>();
        List<? extends Terms.Bucket> buckets = parsedStringTerms.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            //获取数据
            Aggregations bucketAggregations = bucket.getAggregations();
            ParsedSum sumId = bucketAggregations.get("sum_id");
            ParsedAvg avgId = bucketAggregations.get("avg_id");
            map.put(bucket.getKey().toString(),bucket.getDocCount());
        }
        return map;
    }

}
